package com.executor.gateway.filters.logging;

import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.cloud.gateway.route.Route;
import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
import org.springframework.core.Ordered;
import org.springframework.http.HttpHeaders;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.Map;

/**
 * 访问日志全局过滤器
 * 记录详细的API访问日志，包括请求和响应信息
 * 
 * @author miaoguoxin
 * @date 2024/01/01
 */
@Component
@Slf4j
public class AccessLogGlobalFilter implements GlobalFilter, Ordered {

    private static final String START_TIME_ATTR = "accessLogStartTime";
    private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS");

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        // 记录开始时间
        exchange.getAttributes().put(START_TIME_ATTR, System.currentTimeMillis());
        
        // 记录请求日志
        logRequest(exchange);

        return chain.filter(exchange)
                .doOnSuccess(aVoid -> logResponse(exchange, null))
                .doOnError(throwable -> logResponse(exchange, throwable));
    }

    /**
     * 记录请求日志
     */
    private void logRequest(ServerWebExchange exchange) {
        try {
            ServerHttpRequest request = exchange.getRequest();
            Route route = exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR);
            
            Map<String, Object> requestLog = new HashMap<>();
            requestLog.put("timestamp", LocalDateTime.now().format(DATE_TIME_FORMATTER));
            requestLog.put("type", "REQUEST");
            requestLog.put("traceId", exchange.getAttribute("X-Trace-Id"));
            requestLog.put("spanId", exchange.getAttribute("X-Span-Id"));
            requestLog.put("method", request.getMethod().name());
            requestLog.put("uri", request.getURI().toString());
            requestLog.put("path", request.getPath().value());
            requestLog.put("queryParams", request.getQueryParams());
            requestLog.put("headers", sanitizeHeaders(request.getHeaders()));
            requestLog.put("remoteAddress", getClientIp(request));
            requestLog.put("userAgent", request.getHeaders().getFirst("User-Agent"));
            requestLog.put("contentType", request.getHeaders().getFirst("Content-Type"));
            requestLog.put("contentLength", request.getHeaders().getFirst("Content-Length"));
            
            if (route != null) {
                requestLog.put("routeId", route.getId());
                requestLog.put("routeUri", route.getUri().toString());
            }

            log.info("ACCESS_LOG: {}", JSON.toJSONString(requestLog));
        } catch (Exception e) {
            log.error("Error logging request", e);
        }
    }

    /**
     * 记录响应日志
     */
    private void logResponse(ServerWebExchange exchange, Throwable throwable) {
        try {
            ServerHttpRequest request = exchange.getRequest();
            ServerHttpResponse response = exchange.getResponse();
            Long startTime = exchange.getAttribute(START_TIME_ATTR);
            
            Map<String, Object> responseLog = new HashMap<>();
            responseLog.put("timestamp", LocalDateTime.now().format(DATE_TIME_FORMATTER));
            responseLog.put("type", "RESPONSE");
            responseLog.put("traceId", exchange.getAttribute("X-Trace-Id"));
            responseLog.put("spanId", exchange.getAttribute("X-Span-Id"));
            responseLog.put("method", request.getMethod().name());
            responseLog.put("uri", request.getURI().toString());
            responseLog.put("path", request.getPath().value());
            
            if (response.getStatusCode() != null) {
                responseLog.put("statusCode", response.getStatusCode().value());
                responseLog.put("statusText", response.getStatusCode().getReasonPhrase());
            }
            
            responseLog.put("responseHeaders", sanitizeHeaders(response.getHeaders()));
            
            if (startTime != null) {
                long duration = System.currentTimeMillis() - startTime;
                responseLog.put("duration", duration);
                responseLog.put("durationUnit", "ms");
            }
            
            if (throwable != null) {
                responseLog.put("error", true);
                responseLog.put("errorType", throwable.getClass().getSimpleName());
                responseLog.put("errorMessage", throwable.getMessage());
            } else {
                responseLog.put("error", false);
            }

            log.info("ACCESS_LOG: {}", JSON.toJSONString(responseLog));
        } catch (Exception e) {
            log.error("Error logging response", e);
        }
    }

    /**
     * 清理敏感的请求头信息
     */
    private Map<String, String> sanitizeHeaders(HttpHeaders headers) {
        Map<String, String> sanitizedHeaders = new HashMap<>();
        
        headers.forEach((key, values) -> {
            String headerName = key.toLowerCase();
            
            // 敏感信息脱敏
            if (headerName.contains("authorization") || 
                headerName.contains("token") || 
                headerName.contains("password") ||
                headerName.contains("secret")) {
                sanitizedHeaders.put(key, "***MASKED***");
            } else {
                sanitizedHeaders.put(key, String.join(",", values));
            }
        });
        
        return sanitizedHeaders;
    }

    /**
     * 获取客户端真实IP
     */
    private String getClientIp(ServerHttpRequest request) {
        String xForwardedFor = request.getHeaders().getFirst("X-Forwarded-For");
        if (xForwardedFor != null && !xForwardedFor.isEmpty()) {
            return xForwardedFor.split(",")[0].trim();
        }
        
        String xRealIp = request.getHeaders().getFirst("X-Real-IP");
        if (xRealIp != null && !xRealIp.isEmpty()) {
            return xRealIp;
        }
        
        String xOriginalForwardedFor = request.getHeaders().getFirst("X-Original-Forwarded-For");
        if (xOriginalForwardedFor != null && !xOriginalForwardedFor.isEmpty()) {
            return xOriginalForwardedFor.split(",")[0].trim();
        }
        
        return request.getRemoteAddress() != null ? 
                request.getRemoteAddress().getAddress().getHostAddress() : "unknown";
    }

    @Override
    public int getOrder() {
        // 在追踪ID过滤器之后，其他业务过滤器之前执行
        return Ordered.HIGHEST_PRECEDENCE + 10;
    }
}